/**
 * Copyright (c) 2013-2020 Contributors to the Eclipse Foundation
 *
 * <p> See the NOTICE file distributed with this work for additional information regarding copyright
 * ownership. All rights reserved. This program and the accompanying materials are made available
 * under the terms of the Apache License, Version 2.0 which accompanies this distribution and is
 * available at http://www.apache.org/licenses/LICENSE-2.0.txt
 */
package org.locationtech.geowave.service.grpc;

import java.io.IOException;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.netty.NettyServerBuilder;
import io.grpc.protobuf.services.ProtoReflectionService;

public class GeoWaveGrpcServer {
  private static final Logger LOGGER = LoggerFactory.getLogger(GeoWaveGrpcServer.class.getName());
  private Server server = null;

  private static GeoWaveGrpcServer instance;
  private final ServiceLoader<GeoWaveGrpcServiceSpi> serviceLoader;

  private GeoWaveGrpcServer() {
    serviceLoader = ServiceLoader.load(GeoWaveGrpcServiceSpi.class);
  }

  public static synchronized GeoWaveGrpcServer getInstance() {
    if (instance == null) {
      instance = new GeoWaveGrpcServer();
    }
    return instance;
  }

  /** Start serving requests. */
  public void start(final int port) throws IOException {
    final ServerBuilder<?> builder = NettyServerBuilder.forPort(port);
    builder.addService(ProtoReflectionService.newInstance());
    try {
      final Iterator<GeoWaveGrpcServiceSpi> grpcServices = serviceLoader.iterator();
      while (grpcServices.hasNext()) {
        final GeoWaveGrpcServiceSpi s = grpcServices.next();
        builder.addService(s.getBindableService());
      }
    } catch (final ServiceConfigurationError e) {
      LOGGER.error("Exception encountered initializing services for gRPC server", e);
    }

    server = builder.build();
    server.start();
    LOGGER.info("Server started, listening on " + port);

    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        // Use stderr here since the logger may have been reset
        // by its JVM shutdown hook.
        System.err.println("*** shutting down gRPC server since JVM is shutting down");
        GeoWaveGrpcServer.this.stop();
        System.err.println("*** server shut down");
      }
    });
  }

  /** Stop serving requests and shutdown resources. */
  public void stop() {
    if (server != null) {
      server.shutdown();
    }
  }

  /** Await termination on the main thread since the grpc library uses daemon threads. */
  public void blockUntilShutdown() throws InterruptedException {
    if (server != null) {
      server.awaitTermination();
    }
  }
}
